<?php
declare(strict_types=1);

namespace app\shop\logic;

use app\common\facade\Token;
use app\shop\enums\user\UserStatus;
use app\shop\exception\AuthException;
use app\shop\exception\FailException;
use app\shop\exception\ParamsErrorException;
use app\shop\library\ShopAuth;
use app\shop\library\ShopHelper;
use app\shop\library\ShopLogHelper;
use app\shop\model\user\UserModel;
use ba\Random;
use Composer\Factory;
use EasyWeChat\MiniApp\Application;
use Exception;
use think\facade\Db;
use think\facade\Event;
use Throwable;

class LoginLogic extends ShopAuth
{
    private UserModel $userModel;

    protected string $miniAppId;

    protected string $miniAppSecret;

    public function __construct(array $config = [])
    {
        parent::__construct($config);

        $this->userModel = new UserModel();

        $miniappConfig       = ShopHelper::get_setting_group('miniapp_group');
        $this->miniAppId     = $miniappConfig['miniapp_id'] ?? '';
        $this->miniAppSecret = $miniappConfig['miniapp_secret'] ?? '';

        $officialaccountConfig = ShopHelper::get_setting_group('officialaccount_config');
        $this->offAppId     = $officialaccountConfig['off_app_id'] ?? '';
        $this->offAppSecret = $officialaccountConfig['off_secret'] ?? '';
    }

    /**
     * 小程序登录
     * @param array $params
     * @return array
     * @throws FailException|Throwable
     */
    public function miniAppLogin(array $params): array
    {
        $session = $this->getSessionByCode($params['login_code']);
        ShopLogHelper::info('登录信息解密结果', $session);

        $openid  = $session['openid'] ?? '';
        $unionid = $session['unionid'] ?? '';

        if (!$openid) {
            throw new FailException('登录异常,请刷新重试');
        }

        $mobile = $this->getPhoneByPhoneCode($params['code']);

        return $this->loginByPhone($mobile, $openid, $unionid);
    }

    public function officialLogin($params){
        $userinfo = UserModel::where('openid','=', $params['openid'])->find();
        if(isset($userinfo)){
            return $this->loginHandle($userinfo);
        }
        try {

            $data = [
                'mobile'          => $params['phone'] ?? '',
                'username'        => $params['username'],
                'nickname'        => $params['username'],
                'avatar'          => $params['avatar'] ?? '',
                'status'          => UserStatus::ENABLE,
                'last_login_time' => time(),
                'last_login_ip'   => request()->ip(),
                'source'          => ShopAuth::instance()->getUserSourceClient(),
                'openid'           => $params['openid'] ?? '',
            ];
            $grade_id = Db::name('shop_user_grade')->where('status',1)->value('id');
            if(isset($grade_id)){
                $data['grade_id'] = $grade_id;
            }

            $data = array_merge($data, $params);
            if(isset($params['pid'])){
                $model = new UserModel();
                $data['pid'] = $model->getpid($params['pid']);
            }elseif (isset($params['qrCode'])){
                $qrcode = UserModel::where('code', $params['qrCode'])->value('id');
                if($qrcode){
                    $model = new UserModel();
                    $data['pid'] = $model->getpid($qrcode);
                }
            }
            $user = UserModel::create($data);
//            $cid = Db::name('shop_coupon_center')->where('remain_num'>0)->where('is_newuser',1)->value('id');
//            if($cid){
//                $cmodel = new CouponLogic();
//                @$cmodel->receive(['id'=>$cid,'user_id'=>$user->id]);
//            }
            Event::trigger('UserRegisterEvent', $user);
        } catch (Throwable $e) {
            throw new FailException('创建用户失败', null, $e);
        }

        return $this->loginHandle($user);
    }

    /**
     * 通过手机号登录/注册
     * @param string $mobile
     * @param string $openid
     * @param string $unionid
     * @return array
     * @throws FailException
     */
    public function loginByPhone(string $mobile, string $openid = '', string $unionid = ''): array
    {
        $user = $this->userModel->where('mobile', $mobile)->findOrEmpty();

        if ($user->isEmpty()) {
            // 兼容网站第三方授权登录
            $user = $this->getUserByOauth($unionid);

            if (!$user) {
                $user = $this->createUser([
                    'phone' => $mobile
                ]);
            }
        }

        if ($openid) {
            $user->openid = $openid;
        }
        if ($unionid) {
            $user->unionid = $unionid;
        }
        $user->save();

        return $this->loginHandle($user);
    }

    /**
     * 通过第三方授权登录
     * @param string $unionid
     * @return UserModel|null
     */
    public function getUserByOauth(string $unionid = ''): ?UserModel
    {
        if (!ShopHelper::installed_module('nuxtoauth')) {
            return null;
        }

        $oauthData = Db::name('oauth_log')->where([
            ['uuid', '=', $unionid],
            ['source', 'in', ['wechat_scan', 'wechat_mp']]
        ])->findOrEmpty();
        if ($oauthData->isEmpty()) {
            return null;
        }

        $user = $this->userModel->where('id', $oauthData->user_id)->findOrEmpty();
        if ($user->isEmpty()) {
            return null;
        }

        return $user;
    }

    /**
     * 通过code获取手机号
     * @param string $code
     * @return mixed
     * @throws FailException
     */
    public function getPhoneByPhoneCode(string $code): mixed
    {
        try {
            $app      = new Application([
                'app_id' => $this->miniAppId,
                'secret' => $this->miniAppSecret,
            ]);
            $response = $app->getClient()->postJson('wxa/business/getuserphonenumber', [
                'code' => $code
            ])->toArray();

            if ($response['errcode'] !== 0) {
                throw new Exception($response['errmsg'], $response['errcode']);
            }
        } catch (Throwable $e) {
            throw new FailException('手机号获取失败', null, $e);
        }

        return $response['phone_info']['phoneNumber'];
    }

    /**
     * 添加用户
     * @param array $params
     * @return UserModel
     * @throws FailException
     */
    private function createUser(array $params): UserModel
    {
        try {
            $data = [
                'mobile'          => $params['phone'] ?? '',
                'username'        => $params['username'] ?? $this->getInitialUsername($params['phone']),
                'nickname'        => $params['nickname'] ?? $this->getInitialNickName($params['phone']),
                'status'          => UserStatus::ENABLE,
                'last_login_time' => time(),
                'last_login_ip'   => request()->ip(),
                'source'          => ShopAuth::instance()->getUserSourceClient(),
                'openid'          => $params['openid'] ?? '',
            ];
            $grade_id = Db::name('shop_user_grade')->where('status',1)->value('id');
            if(isset($grade_id)){
                $data['grade_id'] = $grade_id;
            }
            if(isset($params['pid'])){
                $data['pid'] = $this->model->getpid($params['pid']);
            }

            $data = array_merge($data, $params);

            $user = UserModel::create($data);
//            $cid = Db::name('shop_coupon_center')->where('remain_num'>0)->where('is_newuser',1)->value('id');
//            if($cid){
//                $cmodel = new CouponLogic();
//                @$cmodel->receive(['id'=>$cid,'user_id'=>$user->id]);
//            }
            Event::trigger('UserRegisterEvent', $user);
        } catch (Throwable $e) {
            throw new FailException('创建用户失败', null, $e);
        }

        return $user;
    }

    private function getInitialUsername(string $phone): string
    {
        return substr_replace($phone, '****', 3, 4);
    }

    private function getInitialNickName(string $phone): string
    {
        return '用户' . substr($phone, -4);
    }

    /**
     * 刷新Token
     * @param string $token
     * @return array
     * @throws AuthException
     */
    public function refreshToken(string $token): array
    {
        $refreshToken = Token::get($token);

        if (!$refreshToken || $refreshToken['expire_time'] < time()) {
            Token::delete($token);
            throw new AuthException('Token已过期,请重新登录');
        }
        $newToken = Random::uuid();
        if ($refreshToken['type'] == self::REFRESH_TOKEN_TYPE) {
            $baUserToken = request()->server('HTTP_BA_USER_REFRESH', request()->request('Ba-User-Refresh', ''));
            if (!$baUserToken) {
                throw new AuthException('刷新Token非法,请重新登录');
            }
            Token::set($newToken, self::TOKEN_TYPE, $refreshToken['user_id']);
        }

        return [
            'type'  => $refreshToken['type'],
            'token' => $newToken
        ];
    }

    /**
     * 登录处理
     * @param mixed $user
     * @return array
     * @throws FailException
     */
    private function loginHandle(mixed $user): array
    {
        $this->model = $user;
        try {
            if ($this->check()) {
                $this->loginSuccessful();
                $userInfo                  = $this->getUserInfo();
                $userInfo['token']         = $this->getToken();
                $userInfo['refresh_token'] = $this->getRefreshToken();

                return $userInfo;
            } else {
                throw new FailException($this->getError(), 50001);
            }
        } catch (AuthException $e) {
            throw new FailException('登录失败', null, $e);
        }
    }

    /**
     * 通过code换取解密信息
     * @param string $code
     * @return array
     * @throws FailException
     */
    public function getSessionByCode(string $code): array
    {
        try {
            $result = (new Application([
                'app_id' => $this->miniAppId,
                'secret' => $this->miniAppSecret,
            ]))->getUtils()->codeToSession($code);
        } catch (Throwable $e) {
            throw new FailException('登录信息解密异常', null, $e);
        }

        return $result;
    }

    /**
     * 通过登录code快捷登录
     * @param string $loginCode 登录code
     * @return array
     * @throws FailException
     */
    public function loginByLoginCode(string $loginCode): array
    {
        $session = $this->getSessionByCode($loginCode);
        $openid  = $session['openid'] ?? '';

        $user = UserModel::where('openid', $openid)->findOrEmpty();

        if ($user->isEmpty()) {
            return [];
        } else {
            return $this->loginHandle($user);
        }
    }

    /**
     * 通过密码登录
     * @param array $params
     * @return array
     * @throws FailException
     * @throws ParamsErrorException
     */
    public function loginByPassword(array $params): array
    {
        $user = $this->userModel->where('username|mobile', $params['username'])->findOrEmpty();

        if ($user->isEmpty()) {
            throw new ParamsErrorException('用户不存在，请先注册');
        }
        if ($user->password != encrypt_password($params['password'], $user->salt)) {
            throw new ParamsErrorException('密码错误');
        }

        return $this->loginHandle($user);
    }

    /**
     * 注册
     * @param array $params
     * @return array
     * @throws FailException
     */
    public function register(array $params): array
    {
        $params['salt']     = Random::build('alnum', 16);
        $params['password'] = encrypt_password($params['password'], $params['salt']);

        $user = $this->createUser($params);

        return $this->loginHandle($user);
    }
}
